<?php

class Lock {

	/**
	* Flag "is locked"
	*/
	private $lockFlag = false;

	/**
	* Flag "print debug messages or not"
	*/
	private $debug = false;

	/**
	* Is lock setted already or not by someone else ?
	*/

	private $alreadyLockedBySomeoneElse = false;

	/**
	* Path to directory with .lock files.
	*/
	private $pathToLocks;

	/**
	* If file already locked - exit. Else set lock.
	*/
	function __construct($pathToLocks = "", $lock_lease_time = 0) {
		if ($pathToLocks == "")
			return;

		$this->pathToLocks = $pathToLocks;
		if (!@is_dir($this->pathToLocks)) @mkdir($this->pathToLocks, 0755, true);
		if ($this->isLocked($lock_lease_time) == false) {
			if ($this->alreadyLockedBySomeoneElse == false) {
				$this->setLock();
			}
		}
	}

	function log_this($msg) {
		if ($this->debug)
			echo $msg;
	}

	/**
	* Unset lock.
	*/
	function __destruct() {
		if ($this->lockFlag) {
			$this->unsetLock();
		}
	}


	/**
	* Returns boolean. TRUE - locked be my, FALSE - not locked by me
	*/
	function isLocked($lock_lease_time = 0) {
		$this->log_this("\n\n!!!!!!!!!!!!!!!!!!!!!! CHECKING LOCK !!!!!!!!!!!!!!!!!!!!!!!!!\n");
		$lockFile = $this->getLockFileName();

		$this->log_this("Lock file: ".$lockFile."\n");

		if (file_exists($lockFile)) {
			$this->log_this("Lock file is exists\n");

			$proc_from_file = trim(file_get_contents($lockFile));

			if ($proc_from_file == getmypid()) {
				$this->log_this("Lock file contains my pid - lock is setted by me\n");
				return true;
			}

			ob_start();

			system("ps ".$proc_from_file);
			$res = ob_get_contents();

			ob_end_clean();

			$res = trim($res);
			$res = explode("\n", $res);
			$this->log_this("PS AX count: ".count($res)."\n");

			if (count($res) == 1) {
				$this->log_this("Proc contains one line - Lock is old. I can set my own lock\n");
				@unlink($lockFile);
				return false;
			}

			$this->log_this("Proc contains two lines - check lease time\n");

			// Proc contains two lines - check lease time
			if ($lock_lease_time == 0) {
				$this->log_this("Lease is zero, so it's bad ( Someone is already got lock file\n");
				$this->alreadyLockedBySomeoneElse = true;
				return false;
			}

			$this->log_this("Lease is: ".$lock_lease_time."\n");

			$stat = stat($lockFile);

			if ((time() - $stat["ctime"]) > $lock_lease_time) {
				// Lock is taken to long time.
				// Kill proc, which holds lock file, and
				// set ne wlock

				/// !!!!!!!!
				/// Be very carefull - this code may kill
				/// useful running proccess !!!!
				/// Think that you do !

				$this->log_this("Kill proc by number: ".$proc_from_file."\n");
				system("kill ".$proc_from_file);

				// Now, do the locking check once again
				// Proccess should be killed, and lock should be setted
				// If not - your deal
				$this->log_this("Ok, now try once againt to set lock. But this time with zero lease time\n");
				return $this->isLocked(0);
			}

			$this->log_this("Lease time is not comming yet. Waiting...\n");
			return true;
		}

		$this->log_this("File doesn't exists - Lock is not setted yet by me. I can set my own one\n");
		return false;
	}

	/**
	* Returns filename for lock-file.
	*/
	function getLockFileName() {
		$file = preg_replace('!^(.+)\.[a-z0-9]+$!i', '\\1.lock', basename($_SERVER['PHP_SELF']));
		return $this->pathToLocks.'/'.$file;
	}


	/**
	* Create .lock file for this process (start lock-session)
	*/
	function setLock() {
		$lockFile = $this->getLockFileName();

		$fp = @fopen($lockFile, "w+");
		if (!$fp) {
			return false;
		}
		$res = @fwrite($fp, getmypid());
		if (!$res) {
			fclose($fp);
			@unlink($lockFile);
			return false;
		}

		fclose($fp);
		@chmod($lockFile, 0666);
		$this->lockFlag = true;

		return true;
	}

	/**
	* Delete .lock file for this process.
	*/
	function unsetLock() {
		return @unlink($this->getLockFileName());
	}

}

?>
